/*!
 * Copyright © 2016-2017 European Support Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

import React from 'react';
import PropTypes from 'prop-types';
import Select from 'react-select';

import Common from '../../../../../../common/Common';
import Logger from '../../../../../../common/Logger';

import Icon from '../../../../../icons/Icon';
import iconSettings from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/settings.svg';
import iconExpanded from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/expanded.svg';
import iconCollapsed from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/collapsed.svg';
import iconOccurrenceDefault from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-default.svg';
import iconOccurrenceStart from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-start.svg';
import iconOccurrenceStop from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-stop.svg';
import iconFragmentDefault from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-default.svg';
import iconFragmentStart from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-start.svg';
import iconFragmentStop from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-stop.svg';

/**
 * Action menu view.
 */
class Actions extends React.Component {
    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Construct view.
     * @param props element properties.
     * @param context react context.
     */
    constructor(props, context) {
        super(props, context);

        Logger.noop();

        this.state = {
            id: undefined,
            visible: false
        };

        // Bindings.

        this.show = this.show.bind(this);

        this.onClickOccurrenceToggle = this.onClickOccurrenceToggle.bind(this);
        this.onClickOccurrenceFrom = this.onClickOccurrenceFrom.bind(this);
        this.onClickOccurrenceTo = this.onClickOccurrenceTo.bind(this);

        this.onClickFragmentToggle = this.onClickFragmentToggle.bind(this);
        this.onChangeFragmentGuard = this.onChangeFragmentGuard.bind(this);
        this.onChangeFragmentOperator = this.onChangeFragmentOperator.bind(
            this
        );

        this.onMouseOut = this.onMouseOut.bind(this);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Show for message.
     * @param id message ID.
     * @param position xy coordinates.
     */
    show(id, position) {
        const message = this.props.model.getMessageById(id);

        let occurrencesToggle = false;
        let fragmentToggle = false;
        if (message) {
            message.occurrences = message.occurrences || {
                start: [],
                stop: []
            };
            message.occurrences.start = message.occurrences.start || [];
            message.occurrences.stop = message.occurrences.stop || [];
            message.fragment = message.fragment || {};
            message.fragment.start = message.fragment.start || false;
            message.fragment.stop = message.fragment.stop || false;
            message.fragment.guard = message.fragment.guard || '';
            message.fragment.operator = message.fragment.operator || '';

            const mo = message.occurrences;
            occurrencesToggle = mo.start.length > 0 || mo.stop.length > 0;

            const mf = message.fragment;
            fragmentToggle = mf.start || mf.stop;
        }

        this.setState({
            id,
            message,
            occurrencesToggle,
            fragmentToggle,
            visible: true,
            x: position.x,
            y: position.y
        });
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Toggle occurrence state.
     */
    onClickOccurrenceToggle() {
        const message = this.state.message;
        if (message) {
            const oFromState = Actions.getOccurrenceState(
                message.occurrences,
                message.from
            );
            const oToState = Actions.getOccurrenceState(
                message.occurrences,
                message.to
            );
            const oExpanded = oFromState > 0 || oToState > 0;
            if (oExpanded) {
                this.setState({ occurrencesExpanded: true });
            } else {
                const occurrencesExpanded = !this.state.occurrencesExpanded;
                this.setState({ occurrencesExpanded });
            }
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Handle menu click.
     */
    onClickOccurrenceFrom() {
        const message = this.state.message;
        if (message) {
            Actions._toggleOccurrence(message.occurrences, message.from);
        }
        this.setState({ message });
        this.props.application.renderDiagram();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Handle menu click.
     */
    onClickOccurrenceTo() {
        const message = this.state.message;
        if (message) {
            Actions._toggleOccurrence(message.occurrences, message.to);
        }
        this.setState({ message });
        this.props.application.renderDiagram();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Toggle fragment.
     */
    onClickFragmentToggle() {
        const message = this.state.message;
        if (message) {
            Actions._toggleFragment(message.fragment);
        }
        this.setState({ message });
        this.props.application.renderDiagram();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Handle menu click.
     * @param event update event.
     */
    onChangeFragmentGuard(event) {
        const message = this.state.message;
        if (message) {
            const options = this.props.application.getOptions();
            message.fragment.guard = Common.sanitizeText(
                event.target.value,
                options,
                'guard'
            );
        }
        this.setState({ message });
        this.props.application.renderDiagram();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Handle menu click.
     * @param value updated value.
     */
    onChangeFragmentOperator(value) {
        const message = this.state.message;
        if (message) {
            message.fragment.operator = value.value;
        }
        this.setState({ message });
        this.props.application.renderDiagram();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Handle mouse movement.
     */
    onMouseOut() {
        this.setState({ id: -1, visible: false, x: 0, y: 0 });
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Render view.
     * @returns {XML}
     */
    render() {
        const actionsStyles = {};
        const message = this.state.message;
        if (!message || !this.state.visible) {
            // Invisible.

            return <div className="asdcs-actions" />;
        }

        // Position and display.

        actionsStyles.display = 'block';
        actionsStyles.left = this.state.x - 10;
        actionsStyles.top = this.state.y - 10;

        const oFromState = Actions.getOccurrenceState(
            message.occurrences,
            message.from
        );
        const oToState = Actions.getOccurrenceState(
            message.occurrences,
            message.to
        );
        const fState = Actions.getFragmentState(message.fragment);

        const oExpanded =
            this.state.occurrencesExpanded || oFromState > 0 || oToState > 0;
        const oAuxClassName = oExpanded ? '' : 'asdcs-hidden';

        const fExpanded = fState !== 0;
        const fAuxClassName = fExpanded ? '' : 'asdcs-hidden';

        const fragmentOperatorOptions = [
            {
                value: 'alt',
                label: 'Alternate'
            },
            {
                value: 'opt',
                label: 'Optional'
            },
            {
                value: 'loop',
                label: 'Loop'
            }
        ];

        const operator = message.fragment.operator || 'alt';

        return (
            <div
                className="asdcs-actions"
                style={actionsStyles}
                onMouseLeave={this.onMouseOut}>
                <div className="asdcs-actions-header">
                    <div className="asdcs-actions-icon">
                        <Icon glyph={iconSettings} />
                    </div>
                </div>

                <div className="asdcs-actions-options">
                    <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-occurrence">
                        <div
                            className="asdcs-actions-option asdcs-actions-option-occurrence-toggle"
                            onClick={this.onClickOccurrenceToggle}>
                            <span className="asdcs-label">Occurrence</span>
                            <div className="asdcs-actions-state">
                                <Icon
                                    glyph={iconCollapsed}
                                    className={oExpanded ? 'asdcs-hidden' : ''}
                                />
                                <Icon
                                    glyph={iconExpanded}
                                    className={oExpanded ? '' : 'asdcs-hidden'}
                                />
                            </div>
                        </div>
                    </div>

                    <div
                        className={`asdcs-actions-option asdcs-actions-option-occurrence-from ${oAuxClassName}`}
                        onClick={this.onClickOccurrenceFrom}>
                        <span className="asdcs-label">From</span>
                        <div className="asdcs-actions-state">
                            <span className="asdcs-annotation" />
                            <Icon
                                glyph={iconOccurrenceDefault}
                                className={
                                    oFromState === 0 ? '' : 'asdcs-hidden'
                                }
                            />
                            <Icon
                                glyph={iconOccurrenceStart}
                                className={
                                    oFromState === 1 ? '' : 'asdcs-hidden'
                                }
                            />
                            <Icon
                                glyph={iconOccurrenceStop}
                                className={
                                    oFromState === 2 ? '' : 'asdcs-hidden'
                                }
                            />
                        </div>
                    </div>

                    <div
                        className={`asdcs-actions-option asdcs-actions-option-occurrence-to ${oAuxClassName}`}
                        onClick={this.onClickOccurrenceTo}>
                        <span className="asdcs-label">To</span>
                        <div className="asdcs-actions-state">
                            <span className="asdcs-annotation" />
                            <Icon
                                glyph={iconOccurrenceDefault}
                                className={oToState === 0 ? '' : 'asdcs-hidden'}
                            />
                            <Icon
                                glyph={iconOccurrenceStart}
                                className={oToState === 1 ? '' : 'asdcs-hidden'}
                            />
                            <Icon
                                glyph={iconOccurrenceStop}
                                className={oToState === 2 ? '' : 'asdcs-hidden'}
                            />
                        </div>
                    </div>

                    <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-fragment">
                        <div
                            className="asdcs-actions-option asdcs-actions-fragment-toggle"
                            onClick={this.onClickFragmentToggle}>
                            <span className="asdcs-label">Fragment</span>
                            <div className="asdcs-actions-state">
                                <span className="asdcs-annotation" />
                                <Icon
                                    glyph={iconFragmentDefault}
                                    className={
                                        fState === 0 ? '' : 'asdcs-hidden'
                                    }
                                />
                                <Icon
                                    glyph={iconFragmentStart}
                                    className={
                                        fState === 1 ? '' : 'asdcs-hidden'
                                    }
                                />
                                <Icon
                                    glyph={iconFragmentStop}
                                    className={
                                        fState === 2 ? '' : 'asdcs-hidden'
                                    }
                                />
                            </div>
                        </div>
                    </div>

                    <div
                        className={`asdcs-actions-option asdcs-actions-fragment-operator ${fAuxClassName}`}>
                        <div className="asdcs-label">Operator</div>
                        <div className="asdcs-value">
                            <Select
                                className="asdcs-editable-select"
                                openOnFocus
                                clearable={false}
                                searchable={false}
                                value={operator}
                                onChange={this.onChangeFragmentOperator}
                                options={fragmentOperatorOptions}
                            />
                        </div>
                    </div>

                    <div
                        className={`asdcs-actions-option asdcs-actions-fragment-guard ${fAuxClassName}`}>
                        <div className="asdcs-label">Guard</div>
                        <div className="asdcs-value">
                            <input
                                className="asdcs-editable"
                                type="text"
                                size="20"
                                maxLength="80"
                                value={message.fragment.guard}
                                placeholder="Condition"
                                onChange={this.onChangeFragmentGuard}
                            />
                        </div>
                    </div>
                </div>

                <div className="asdcs-actions-footer" />
            </div>
        );
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Toggle through three occurrence states on click.
     * @param occurrence occurrences state, updated as side-effect.
     * @param lifelineId message end that's being toggled.
     * @private
     */
    static _toggleOccurrence(occurrence, lifelineId) {
        const o = occurrence;

        const rm = function rm(array, value) {
            const index = array.indexOf(value);
            if (index !== -1) {
                array.splice(index, 1);
            }
        };

        const add = function add(array, value) {
            if (array.indexOf(value) === -1) {
                array.push(value);
            }
        };

        if (o.start && o.start.indexOf(lifelineId) !== -1) {
            // Start -> stop.
            rm(o.start, lifelineId);
            add(o.stop, lifelineId);
        } else if (o.stop && o.stop.indexOf(lifelineId) !== -1) {
            // Stop -> default.
            rm(o.start, lifelineId);
            rm(o.stop, lifelineId);
        } else {
            // Default -> start.
            add(o.start, lifelineId);
            rm(o.stop, lifelineId);
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Toggle fragment setting on click.
     * @param fragment
     * @private
     **/
    static _toggleFragment(fragment) {
        const f = fragment;
        if (f.start === true) {
            f.start = false;
            f.stop = true;
        } else if (f.stop === true) {
            f.stop = false;
            f.start = false;
        } else {
            f.start = true;
            f.stop = false;
        }
        f.guard = '';
        f.operator = '';
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Get ternary occurrences state.
     * @param o occurrences.
     * @param lifelineId from/to lifeline ID.
     * @returns {number}
     * @private
     */
    static getOccurrenceState(o, lifelineId) {
        if (o.start.indexOf(lifelineId) !== -1) {
            return 1;
        }
        if (o.stop.indexOf(lifelineId) !== -1) {
            return 2;
        }
        return 0;
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Get ternary fragment state.
     * @param f fragment.
     * @returns {number}
     * @private
     */
    static getFragmentState(f) {
        if (f.start) {
            return 1;
        }
        if (f.stop) {
            return 2;
        }
        return 0;
    }
}

/** Element properties. */
Actions.propTypes = {
    application: PropTypes.object.isRequired,
    model: PropTypes.object.isRequired
};

export default Actions;
